# 动态属性和特性
# 1、使用动态属性转换数据
# @see osconfeed.py
# 使用动态属性访问JSON类数据
# 特殊方法 __new__ 类方法 必须返回一个实例
# __new__ 方法也可以返回其他类的实例，此时，解释器不会调用 __init__ 方法。
# Python 构建对象的过程伪代码
def object_maker(the_class, some_arg):
    new_object = the_class.__new__(some_arg)
    if isinstance(new_object, the_class):
        the_class.__init__(new_object, some_arg)
    return new_object


# shelve模块调整OSCON数据源的结构 @see schedule1.py @ see schedule2.py
# shelve.open 高阶函数返回一个 shelve.Shelf 实例，这是简单的键值对象数据库，背后由 dbm 模块支持

# 2、使用特性验证属性
# @see bulkfood_v1.py
# 3、特性全解析
# 虽然内置的 property 经常用作装饰器，但它其实是一个类
# 构造方法与调用工厂函数没有区别，只要能返回新的可调用对象，代替被装饰的函数，二者都可以用作装饰器

# 特性都是类属性，但是特性管理的其实是实例属性的存取
# 实例和所属的类有同名数据属性，那么实例属性会覆盖（或称遮盖）类属性
class Class:
    data = 'the class data attr'

    @property
    def prop(self):
        return 'the prop value'


obj = Class()
print(vars(obj))
print(obj.data)  # 获取的是 Class.data 的值
obj.data = 'bar'  # 创建一个实例属性
print(vars(obj))  # 实例属性
print(obj.data)
print(Class.data)  # Class.data 属性的值完好无损。
print(Class.prop)  # 直接从 Class 中读取 prop 特性，获取的是特性对象本身，不会运行特性的读值方法
print(obj.prop)  # 执行特性的读值方法 the prop value
# obj.prop = 'foo'
# AttributeError: property 'prop' of 'Class' object has no setter
obj.__dict__['prop'] = 'foo'
print(vars(obj))
print(obj.prop)  # 读取 obj.prop 时仍会运行特性的读值方法。特性没被实例属性遮盖 the prop value
Class.prop = 'baz'  # 覆盖 Class.prop 特性，销毁特性对象
print(obj.prop)  # obj.prop 获取的是实例属性 foo
print(obj.data)  # 实例属性data
print(Class.data)  # 类属性 data
Class.data = property(lambda self: 'the "data" prop value')  # 新特性覆盖 Class.data
print(obj.data)  # obj.data 被 Class.data 特性遮盖了
del Class.data
print(obj.data)  # 现在恢复原样，obj.data 获取的是实例属性 data
# obj.attr 这样的表达式不会从 obj 开始寻找attr
# 而是从 obj.__class__ 开始，而且，仅当类中没有名为 attr 的特性时，Python 才会在 obj 实例中寻找

# 4、定义一个特性工厂函数
# @see bulkfood_v2prop.py

# 5、处理属性删除操作
# 使用 Python 编程时不常删除属性，通过特性删除属性更少见。但是，Python 支持这么做
# @see blacknight.py

# 6、处理属性的重要属性和函数
# 6.1影响属性处理方式的特殊属性
# __class__
# 对象所属类的引用（即 obj.__class__ 与 type(obj) 的作用相同）
# Python 的某些特殊方法，例如 __getattr__，只在对象的类中寻找，而不在实例中寻找。
# __dict__
# 一个映射，存储对象或类的可写属性。有 __dict__ 属性的对象，任何时候都能随意设置新属性。
# 如果类有 __slots__ 属性，它的实例可能没有 __dict__ 属性。
# __slots__
# 类可以定义这个这属性，限制实例能有哪些属性。__slots__ 属性的值是一个字符串组成的元组，指明允许有的属性。
# 如果_slots__ 中没有 '__dict__'，那么该类的实例没有 __dict__ 属性，实例只允许有指定名称的属性。
# 6.2处理属性的内置函数
# dir([object])
# 列出对象的大多数属性，dir 函数的目的是交互式使用，因此没有提供完整的属性列表，只列出一组“重要的”属性名
# dir 函数能审查有或没有 __dict__ 属性的对象。dir 函数不会列出 __dict__ 属性本身，但会列出其中的键
# dir 函数也不会列出类的几个特殊属性，例如 __mro__、__bases__ 和 __name__。
# 如果没有指定可选的 object 参数，dir 函数会列出当前作用域中的名称。
# getattr(object, name[, default])
# 从 object 对象中获取 name 字符串对应的属性。获取的属性可能来自对象所属的类或超类
# 如果没有指定的属性，getattr 函数抛出AttributeError 异常，或者返回 default 参数的值（如果设定了这个参数的话）。
# hasattr(object, name)
# 如果 object 对象中存在指定的属性，或者能以某种方式（例如继承）通过 object 对象获取指定的属性，返回 True。
# 这个函数的实现方法是调用 getattr(object, name) 函数，看看是否抛出AttributeError 异常。”
# setattr(object, name, value)
# 把 object 对象指定属性的值设为 value，前提是 object 对象能接受那个值，这个函数可能会创建一个新属性，或者覆盖现有的属性。
# vars([object])
# 返回 object 对象的 __dict__ 属性；如果实例所属的类定义了__slots__ 属性，实例没有 __dict__ 属性
# 那么 vars 函数不能处理那个实例（相反，dir 函数能处理这样的实例）
# 如果没有指定参数， 那么 vars() 函数的作用与 locals() 函数一样：返回表示本地作用域的字典
# 6.3 处理属性的特殊方法
# 使用点号或内置的 getattr、hasattr 和 setattr 函数存取属性都会触发下述列表中相应的特殊方法
# 但是，直接通过实例的 __dict__ 属性读写属性不会触发这些特殊方法——如果需要，通常会使用这种方式跳过特殊方法
# __delattr__(self, name)
# 只要使用 del 语句删除属性，就会调用这个方法。例如，del obj.attr 语句触发 Class.__delattr__(obj, 'attr') 方法
# __dir__(self)
# 把对象传给 dir 函数时调用，列出属性。例如，dir(obj) 触发 Class.__dir__(obj) 方法
# __getattr__(self, name)
# 仅当获取指定的属性失败，搜索过 obj、Class 和超类之后调用，表达式 obj.no_such_attr、getattr(obj, 'no_such_attr') 和
# hasattr(obj, 'no_such_attr') 可能会触发Class.__getattr__(obj, 'no_such_attr') 方法
# 但是，仅当在obj、Class 和超类中找不到指定的属性时才会触发
# __getattribute__(self, name)
# 尝试获取指定的属性时总会调用这个方法，不过，寻找的属性是特殊属性或特殊方法时除外。
# 点号与 getattr 和 hasattr 内置函数会触发这个方法。调用 __getattribute__ 方法且抛出 AttributeError
# 异常时，才会调用 __getattr__ 方法。为了在获取 obj 实例的属性时不导致无限递归，__getattribute__ 方法的实现要使用
# super().__getattribute__(obj, name)
# __setattr__(self, name, value)
# 尝试设置指定的属性时总会调用这个方法。点号和 setattr 内置函数会触发这个方法。
# 例如，obj.attr = 42 和 setattr(obj,'attr', 42) 都会触发 Class.__setattr__(obj,‘attr’, 42) 方法
